﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Blog.TemplateEngine.Helpers;

namespace Blog.TemplateEngine.Functions
{
    class ForFunction : IFunction
    {
        protected INode Compile(Parser parser, Token token)
        {
            string[] source = token.Content.SplitAndTrim(' ');
            if (source.Length < 5)
            {
                throw new TemplateException("'for' statements should have at least 5 parameters: for 'var' 'val' to 'val' [step 'val']");
            }
            int lbound, ubound, step = 1;
            if (!Int32.TryParse(source[2], out lbound))
            {
                throw new TemplateException("Invalid lower bound for 'for' statement: " + source[2]);
            }
            if (!Int32.TryParse(source[4], out ubound))
            {
                throw new TemplateException("Invalid upper bound for 'for' statement: " + source[4]);
            }
            if (source.Length >= 7 && !Int32.TryParse(source[6], out step))
            {
                throw new TemplateException("Invalid step for 'for' statement: " + source[6]);
            }

            NodeList body = parser.Parse(new string[] { "end" });
            parser.DeleteNextToken();

            return new ForNode(body, source[1], lbound, ubound, step);
        }

        INode IFunction.Compile(Parser parser, Token token)
        {
            return Compile(parser, token);
        }
    }

    class ForNode : INode
    {
        NodeList mBody;
        string mVariable;
        int mLowBound, mUpperBound, mStep; 

        public ForNode(NodeList body, string variable, int lbound, int ubound, int step)
        {
            mBody = body;
            mVariable = variable;
            mLowBound = lbound;
            mUpperBound = ubound;
            mStep = step;
        }

        protected string Render(Context context)
        {
            context.Push();
            var result = new StringBuilder();
            if (mLowBound > mUpperBound)
            {
                for (var i = mLowBound; i >= mUpperBound; i += mStep)
                {
                    context[mVariable] = i;
                    result.Append(mBody.Render(context));
                }
            }
            else
            {
                for (var i = mLowBound; i <= mUpperBound; i += mStep)
                {
                    context[mVariable] = i;
                    result.Append(mBody.Render(context));
                }
            }
            context.Pop();

            return result.ToString();
        }

        string INode.Render(Context context)
        {
            return Render(context);
        }
    }
}
